package com.zzl.java8.lambda1;

import com.zzl.java8.lambda3.Student;
import org.junit.jupiter.api.Test;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;

/**
 * 参考：https://blog.csdn.net/weixin_36133625/article/details/116903121
 * 参考：https://blog.csdn.net/weixin_44657829/article/details/110871752
 * description: lambda表达式
 * @author 朱正磊
 * @date 2021-10-12 13:13:44
 */
public class LambdaTest1 {

    /**
     * description: 创建Stream的方法的5种方式
     * @author 朱正磊
     * @date 2021-10-13 10:09:16
     */
    @Test
    void test1() {
        // 方式一：Collection接口的方法  集合
        Collection<String> collection = new ArrayList<>();
        collection.add("123");
        collection.add("abc");
        // 获取串行流
        Stream<String> stream = collection.stream();
        // 获取并行流
        Stream<String> parallelStream = collection.parallelStream();

        // 方式二：通过Arrays中的Stream方法  数组
        IntStream intStream1 = Arrays.stream(new int[]{1, 2, 3, 4});

        // 方式三：Stream中的of方法  静态方法
        Stream<String> stringStream1 = Stream.of("zxc", "f3e", "222");

        Stream<String> stringStream2 = Stream.of();

        Stream<String> stringStream3 = Stream.of("zxc");

        String[] stringArray = new String[]{"1", "2", "3", "4"};
        Stream<String> stringStream4 = Stream.of(stringArray);

        int[] intArray1 = new int[]{11, 22, 33, 44};
        Stream<int[]> intStream2 = Stream.of(intArray1);

        Integer[] intArray2 = new Integer[]{111, 222, 333, 444};
        Stream<Integer> intStream3 = Stream.of(intArray2);

        Map<String, Object> map = new HashMap<String, Object>(){{
            put("01", "今彩慧");
            put("02", 2022);
            put("03", true);
        }};
        Stream<Map<String, Object>> mapStream = Stream.of(map);

        Stream<Collection<String>> collectionStream = Stream.of(collection);

        // 方式四：Stream中的iterate方法  创建无限流  （结果是无限个） 使用limit可以限制元素个数
        // 0 - 99
        Stream<Integer> intStream4 = Stream.iterate(0, x -> x + 1).limit(100);

        // 方式五：IntStream.range方法
        // 0 - 99
        IntStream rangeStream = IntStream.range(0, 100);
    }

    /**
     * Stream filter(Predicate<?super T> predicate)返回由与此给定谓词匹配的此流的元素组成的流。
     * ---->接收Lambda，从流中排除某些元素。
     * description: 中间操作 - filter
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test2() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        // 1、创建Stream
        Stream<Student> stream = list.stream();

        // 2、filter方法（找到年龄大于等于14岁的学生）
        stream = stream.filter(student -> student.getAge() >= 14);

        // 3、终止操作;如果没有终止操作的话，上面的第二步中间操作不执行
        stream.forEach(System.out::println);

        // 注意：如果仅仅只执行1、2操作的话，不会有任何结果。
        // 验证出Steam操作是延迟的，只有进行了终止操作，才会执行中间操作！这就是所谓的延迟加载
    }

    /**
     * Stream limit(Long maxSize)返回由该流的元素组成的流，截断长度不能超过maxSize. 只要找到maxSize个满足条件的即可。
     * ---->截断流，使其元素不超过给定的数量。
     * description:  中间操作 - limit
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test3() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        // limit方法，短路(效率增高)，只要找到了2个满足条件的，后面的迭代操作就不再执行了！
        list.stream().filter(x -> x.getAge() >= 14).limit(2).forEach(System.out::println);
    }

    /**
     * Stream skip(Long n)在丢掉流的前n个元素后，返回由该流剩下的元素组成的流，如果此流包含少于n个元素，那么将返回一个空流。
     * ---->跳过元素，返回一个扔掉了前n个元素的流。如果流中的元素不足n个，则返回一个空流，与limit(n)互补。
     * description: 中间操作 - skip
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test4() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        // skip方法跳过前2个满足条件的，留下后面满足条件的结果！
        list.stream().filter(x -> x.getAge() >= 14).skip(2).forEach(System.out::println);
    }

    /**
     * 自定义的类在去重的过程中必须重写hashCode和equals方法，因为distinct实现的时候底层去找这两个方法。
     * description: 中间操作 - distinct
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test5() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小王", 14, "1501"));
        list.add(new Student("2015134001", "小王", 14, "1501"));
        list.add(new Student("2015134002", "小梁", 15, "1502"));
        list.add(new Student("2015134002", "小梁", 15, "1502"));

        // distinct，去重操作！
        list.stream().distinct().forEach(System.out::println);
    }

    /**
     * 如果需要将流中的元素映射到另一个流中，可以使用map方法。方法签名：Stream map(Function<? super T, ? extends R> mapper);
     * 该接口需要一个Function函数式接口参数，可以将当前流中的T类型数据转换为另一种R类型的流。
     * description: 中间操作 - map
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test6() {
        Stream<String> stream = Stream.of("11", "22", "33");
        // map方法的参数通过方法引用，将字符串类型转换成为了int类型（并自动装箱为Integer类对象）。
        Stream<Integer> result = stream.map(Integer::parseInt);
        result.forEach(x -> System.out.println(x + 10));
    }

    /**
     * （1）Stream sorted()返回此流元素组成的流，根据自然顺序排序。底层按照内部比较器进行排序，实现Comparable接口中的compareTo方法。
     * （2）Stream sorted(Comparator<?super T>comparator) 返回由此元素组成的流，根据挺的Comparator进行顺序排序。指定顺序。
     *     指定排序策略：底层按照外部比较器进行排序 Comparator接口一定要重新Compare方法。
     * description: 中间操作 - sorted
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test7() {
        System.out.println("=========================【自然排序】=========================");

        Stream<Integer> stream11 = Stream.of(87, 76, 2, 98, 13);
        // 自然排序
        Stream<Integer> stream1 = stream11.sorted();
        stream1.forEach(System.out::println);

        System.out.println("=========================【自定义比较器排序（顺序排序）】=========================");

        Stream<Integer> stream22 = Stream.of(87, 76, 2, 98, 13);
        // 自定义比较器排序（顺序排序）
        // Stream<Integer> stream2 = stream22.sorted((o1, o2) -> o1 - o2);
        Stream<Integer> stream2 = stream22.sorted(Comparator.comparingInt(o -> o));
        stream2.forEach(System.out::println);

        System.out.println("=========================【自定义比较器排序（倒序排序）】=========================");

        Stream<Integer> stream33 = Stream.of(87, 76, 2, 98, 13);
        // 自定义比较器排序（倒序排序）
        Stream<Integer> stream3 = stream33.sorted((o1, o2) -> o2 - o1);
        stream3.forEach(System.out::println);
    }

    /**
     * 查找(find)
     * 如果需要找到某些数据，可以使用find相关方法。方法签名：
     * Optional findFirst();
     * Optional findAny();
     * description: 终止操作 - find
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test8() {
        Optional<Integer> first = Stream.of(23, 67, 32, 9, 55).findFirst();
        // 23
        System.out.println(first.get());
        // 23
        System.out.println(first.orElse(1));

        Optional<Integer> any = Stream.of(5, 67, 32, 9, 55).findAny();
        // 5
        System.out.println(any.get());
        // 5
        System.out.println(any.orElse(1));
    }

    /**
     * 匹配(match)
     * 如果需要判断数据是否匹配指定的条件，可以使用Match相关方法。方法签名：
     * boolean allMatch(Predicate<? super T> predicate);
     * boolean anyMatch(Predicate<? super T> predicate);
     * boolean noneMatch(Predicate<? super T> predicate);
     * description: 终止操作 - match
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test9() {
        // allMatch: 元素是否全部满足条件
        boolean allMatch = Stream.of(23, 12, 4, 87, 99).allMatch(x -> x > 50);
        // allMatch = false
        System.out.println("allMatch = " + allMatch);

        // anyMatch: 元素是否任意有一个满足条件
        boolean anyMatch = Stream.of(23, 12, 4, 87, 99).anyMatch(x -> x > 50);
        // anyMatch = true
        System.out.println("anyMatch = " + anyMatch);

        // noneMatch: 元素是否全部不满足条件
        boolean noneMatch = Stream.of(23, 12, 4, 87, 99).noneMatch(x -> x > 50);
        // noneMatch = false
        System.out.println("noneMatch = " + noneMatch);
    }

    /**
     * forEach 用来遍历流中的数据
     * description: 终止操作 - foreach
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test10() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小王", 14, "1501"));
        list.add(new Student("2015134001", "小王", 14, "1501"));
        list.add(new Student("2015134002", "小梁", 15, "1502"));
        list.add(new Student("2015134002", "小梁", 15, "1502"));

        list.stream().map(Student::getName).forEach(System.out::println);
    }

    /**
     * description: 终止操作 - max,min
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test11() {
        String[] strArray = new String[]{"zhangsan", "lisi", "wangwu", "zhaoliu"};
        Stream<String> stream1 = Arrays.stream(strArray);
        IntStream intStream1 = stream1.mapToInt(String::length);
        OptionalInt optionalInt1 = intStream1.max();
        boolean present1 = optionalInt1.isPresent();
        if (present1) {
            int max = optionalInt1.getAsInt();
            // max = 8
            System.out.println("max = " + max);
        }

        List<String> strList = Arrays.asList("zhangsan", "lisi", "wangwu", "zhaoliu");
        Stream<String> stream2 = strList.stream();
        IntStream intStream2 = stream2.mapToInt(String::length);
        OptionalInt optionalInt2 = intStream2.min();
        boolean present2 = optionalInt2.isPresent();
        if (present2) {
            int min = optionalInt2.getAsInt();
            // min = 4
            System.out.println("min = " + min);
        }
    }

    /**
     * Stream流提供count方法来统计其中的元素个数：long count();
     * 该方法返回一个long值代表元素个数。
     * description: 终止操作 - count
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test12() {
        List<String> strList = new ArrayList<>();
        Collections.addAll(strList, "孙悟空", "猪八戒", "沙僧", "唐僧");
        // strList = [孙悟空, 猪八戒, 沙僧, 唐僧]
        System.out.println("strList = " + strList);

        long count = strList.stream().filter(e -> e.length() > 2).count();
        // count = 2
        System.out.println("count = " + count);
    }

    /**
     * description: 终止操作 - groupingBy
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test13() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        Map<Integer, List<Student>> map1 = list.stream().collect(Collectors.groupingBy(Student::getAge));
        // 13 = [Student(idNum=2015134001, name=小明, age=13, classNum=1501)]
        // 14 = [Student(idNum=2015134002, name=小王, age=14, classNum=1501), Student(idNum=2015134003, name=小张, age=14, classNum=1502)]
        // 15 = [Student(idNum=2015134004, name=小梁, age=15, classNum=1502)]
        map1.forEach((key, value) -> System.out.println(key + " = " + value));

        Map<String, List<Student>> map2 = list.stream().collect(Collectors.groupingBy(x -> {
            if (x.getAge() > 13) {
                return "大朋友";
            } else {
                return "小朋友";
            }
        }));
        // 小朋友 = [Student(idNum=2015134001, name=小明, age=13, classNum=1501)]
        // 大朋友 = [Student(idNum=2015134002, name=小王, age=14, classNum=1501), Student(idNum=2015134003, name=小张, age=14, classNum=1502), Student(idNum=2015134004, name=小梁, age=15, classNum=1502)]
        map2.forEach((key, value) -> System.out.println(key + " = " + value));
    }

    /**
     * Collectors.joining会根据指定的连接符，将所有元素连接成一个字符串。
     * description: 终止操作 - joining（拼接）
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test14() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        String collect = list.stream().map(Student::getName).collect(Collectors.joining("---"));
        // collect = 小明---小王---小张---小梁
        System.out.println("collect = " + collect);

        String joining = list.stream().map(Student::getName).collect(Collectors.joining("#", "^", "$"));
        // joining = ^小明#小王#小张#小梁$
        System.out.println("joining = " + joining);
    }

    /**
     * 将流中数据收集到集合中
     * description: 终止操作 - toList，toSet
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test15() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        List<String> toList = list.stream().map(Student::getName).collect(Collectors.toList());
        // class java.util.ArrayList
        System.out.println(toList.getClass());

        Set<String> toSet = list.stream().map(Student::getName).collect(Collectors.toSet());
        // class java.util.HashSet
        System.out.println(toSet.getClass());

        LinkedList<String> linkedList = list.stream().map(Student::getName).collect(Collectors.toCollection(LinkedList::new));
        // class java.util.LinkedList
        System.out.println(linkedList.getClass());

        LinkedHashSet<String> linkedHashSet = list.stream().map(Student::getName).collect(Collectors.toCollection(LinkedHashSet::new));
        // class java.util.LinkedHashSet
        System.out.println(linkedHashSet.getClass());
    }

    /**
     * 将流中数据收集到集合中
     * description: 终止操作 - toMap
     * @author 朱正磊
     * @date 2021-10-13 10:51:40
     */
    @Test
    void test16() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));

        // Map<String, Student> map = list.stream().collect(Collectors.toMap(student -> student.getIdNum(), student -> student));
        Map<String, Student> map = list.stream().collect(Collectors.toMap(Student::getIdNum, student -> student));
        // map = {2015134001=Student(idNum=2015134001, name=小明, age=13, classNum=1501), 2015134002=Student(idNum=2015134002, name=小王, age=14, classNum=1501)}
        System.out.println("map = " + map);
    }

    /**
     * description: 终止操作 - reduce（规约）
     * @author 朱正磊
     * @date 2022-06-23 17:20:55
     */
    @Test
    void test17() {
        // 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10
        Integer sum = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10).reduce(0, (x, y) -> x + y);
        // 这里reduce操作中,
        // 起始值为0,
        // 第一次x为0,
        // list中第一个元素1为y
        // 经行(x+y)操作,
        // 然后又把(x+y)的值作为x,
        // list中第二个元素2作为y,
        // 依次累加.最终得到一个sum值
        // sum = 55
        System.out.println("sum = " + sum);

        // 100 + 1 + 2 + 3 + 4 + 5
        Integer sum2 = Stream.of(1, 2, 3, 4, 5).reduce(100, (x, y) -> x + y);
        // sum2 = 115
        System.out.println("sum2 = " + sum2);

        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));
        Integer sum3 = list.stream().map(Student::getAge).reduce(0, (x, y) -> x + y);
        // sum3 = 56
        System.out.println("sum3 = " + sum3);

        Optional<Integer> optional = list.stream().map(Student::getAge).reduce(Integer::sum);
        Integer sum4 = optional.get();
        // sum4 = 56
        System.out.println("sum4 = " + sum4);

        Integer sum5 = optional.orElse(-1);
        // sum5 = 56
        System.out.println("sum5 = " + sum5);
    }

    /**
     *  stream()中的map(Function mapping)返回给定函数应用于此流得元素得结果
     *  stream()中的mapToInt(ToIntFunction mapper)返回一个IntStream其中包含给定函数应用于此流得元素的结果
     *  mapToInt有sum()求和方法
     * description: 中间操作 - mapToInt
     * @author 朱正磊
     * @date 2022-06-23 17:23:11
     */
    @Test
    void test18() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        Stream<Integer> integerStream = list.stream().map(Student::getAge);
        Optional<Integer> optional = integerStream.reduce(Integer::sum);
        Integer sum1 = optional.orElse(-1);
        System.out.println("sum1 = " + sum1);

        IntStream intStream = list.stream().mapToInt(Student::getAge);
        int sum2 = intStream.sum();
        System.out.println("sum2 = " + sum2);
    }

    /**
     * description: 中间操作 - flatMapToInt（扁平化）
     * @author 朱正磊
     * @date 2022-06-23 17:23:17
     */
    @Test
    void test19() {
        // 对整形数组流进行扁平化
        int[] intArray1 = new int[]{1, 2, 3, 4};
        int[] intArray2 = new int[]{5, 6, 7, 8};
        // 生成一个整形数组流
        Stream<int[]> stream1 = Stream.of(intArray1, intArray2);
        Stream<int[]> stream2 = Stream.of(intArray1, intArray2);
        Stream<int[]> stream3 = Stream.of(intArray1, intArray2);
        Stream<int[]> stream4 = Stream.of(intArray1, intArray2);
        Stream<int[]> stream5 = Stream.of(intArray1, intArray2);
        Stream<int[]> stream6 = Stream.of(intArray1, intArray2);

        Stream<Integer> intStream1 = stream1.flatMap(x -> Arrays.stream(x).boxed());
        Stream<Integer> intStream2 = stream2.flatMap(x -> IntStream.of(x).boxed());

        IntStream intStream3 = stream3.flatMapToInt(x -> Arrays.stream(x));
        IntStream intStream4 = stream4.flatMapToInt(Arrays::stream);

        IntStream intStream5 = stream5.flatMapToInt(x -> IntStream.of(x));
        IntStream intStream6 = stream6.flatMapToInt(IntStream::of);

        // 12345678
        intStream6.forEach(System.out::print);
    }

    /**
     * 将所有流中的元素并到一起连接成一个流
     * description: 中间操作 - flatMap（扁平化）
     * @author 朱正磊
     * @date 2022-06-23 17:23:22
     */
    @Test
    void test20() {
        // 对Map中的List进行扁平化
        Map<String, List<String>> map = new HashMap<String, List<String>>(){{
            put("01", Arrays.asList("衣服", "手机", "电脑"));
            put("02", Arrays.asList("篮球鞋", "瑜伽服", "化妆品"));
            put("03", Arrays.asList("眼镜", "手表", "戒指"));
        }};

        Stream<Map<String, List<String>>> mapStream1 = Stream.of(map);
        Stream<List<String>> listStream1 = mapStream1.flatMap(x -> x.values().stream());

        Stream<Map<String, List<String>>> mapStream2 = Stream.of(map);
        Stream<List<String>> listStream2 = mapStream2.flatMap(x -> x.values().stream());

        Stream<String> stringStream1 = listStream1.flatMap(x -> x.stream());
        Stream<String> stringStream2 = listStream2.flatMap(Collection::stream);

        // 衣服
        // 手机
        // 电脑
        // 篮球鞋
        // 瑜伽服
        // 化妆品
        // 眼镜
        // 手表
        // 戒指
        stringStream2.forEach(System.out::println);
    }

    /**
     * peek(Consumer c) 获取流中元素，操作流中元素，与foreach不同的是不会截断流，可继续操作流
     * 尽管有些时候IDEA编译会提示你可将map操作转换成peek，但我们最好不要这么做。
     * description: 中间操作 - peek
     * @author 朱正磊
     * @date 2022-06-23 17:24:46
     */
    @Test
    void test21() {
        List<Student> list = new ArrayList<>();
        list.add(new Student("2015134001", "小明", 13, "1501"));
        list.add(new Student("2015134002", "小王", 14, "1501"));
        list.add(new Student("2015134003", "小张", 14, "1502"));
        list.add(new Student("2015134004", "小梁", 15, "1502"));

        // 小明
        // 小王
        // 小张
        // 小梁
        list.stream().map(Student::getName).peek(System.out::println).collect(Collectors.toList());
    }

}
